/**
 * 
 */
package GUI.GameBoard;

import Monopol.MonopolExceptions.MonopolErrors;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.HeadlessException;
import java.awt.Insets;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.swing.BorderFactory;
import javax.swing.JFrame;
import javax.swing.JPanel;

import GUI.GameBoard.Center.CenterPanel;
import GUI.PlayerPanel.PlayerButton;
import GUI.PlayerPanel.PlayerPanel;
import GUI.PopUpDialog;
import Monopol.Cubes.Cube;
import Monopol.Monopol;
import Player.Player;
import Square.Square;
import Utils.Utils;
import java.awt.Dimension;
import java.awt.Point;

/**
 * @author Avia Tam
 * @author Assaf Miron
 *
 */
@SuppressWarnings("serial")
public class GameBoard extends JFrame {

    private static final int LINE_SIZE = 9;
    private JPanel pnlBoard;
    private PlayerPanel pnlPlayer;
    private CenterPanel pnlCenter;
    private List<GameSquare> components;
    private ArrayList<PlayerButton> playersButton;

    /**
     * @throws HeadlessException
     */
    public GameBoard(Square[] gameSquares, ArrayList<Player> players) throws HeadlessException {
	super();
	initBoard(gameSquares, players);
	initPlayer();
	initUI();
    }

    /**
     * Initialize the Board UI
     */
    private void initUI() {
	// Set the Look and Feel of the Current OS
	Utils.setLookAndFeel();
	// Set the Frame Layout
	this.setLayout(new BorderLayout());
	try {
	    // Add the Player Panel
	    this.add(pnlPlayer, BorderLayout.EAST);
	    // Add the Board Panel
	    this.add(pnlBoard, BorderLayout.CENTER);
	} catch (Exception e) {
	    e.printStackTrace();
	}
	// Set the window's bounds
	int width = 800;
	int height = 600;
	// Set a Preferred size Dimension for the Panel
	Dimension prefSize = new Dimension(width, height);
	// Set the Preferred Size for this Panel
	setPreferredSize(prefSize);
	// Centering the window
	this.setBounds(Utils.posCenterScreen(width, height));
	// Exit on Close
	this.setDefaultCloseOperation(DO_NOTHING_ON_CLOSE);
	// Listen to Window Close and Ask the User if he Wants to Quit
	this.addWindowListener(new WindowAdapter() {

	    @Override
	    public void windowClosing(WindowEvent winEvt) {
		try {
		    if (exitProgram() == EXIT_ON_CLOSE) {
			System.exit(0);
		    }
		} catch (MonopolErrors ex) {
		    Logger.getLogger(GameBoard.class.getName()).log(Level.SEVERE, null, ex);
		}
	    }
	});

	// Repaint and Validate the Player
	Utils.repaintAndValidateAll(pnlPlayer);
	// Repaint and Validate the Board
	Utils.repaintAndValidateAll(pnlBoard);

    }

    /**
     * Initialize the Player Panel
     */
    private void initPlayer() {
	// Check If the Panel is not Creates
	if (pnlPlayer == null) {
	    // Create the Panel
	    pnlPlayer = new PlayerPanel();
	    // Show the Panel
	    pnlPlayer.setVisible(true);
	}
    }

    /**
     * Initialize the Game Board
     * This Part is based on a code from the Class of Liron Blecher
     * @param gameSquares The Game Board Squares
     */
    private void initBoard(Square[] gameSquares, ArrayList<Player> players) {
	// Check if the Panel is not created
	if (pnlBoard == null) {
	    // Create a new Panel
	    pnlBoard = new JPanel();
	    // Initialize layout
	    GridBagLayout gbl = new GridBagLayout();
	    pnlBoard.setLayout(gbl);
	    // Create a Components List to Add
	    components = new LinkedList<GameSquare>();
	    // Create a New Players Array List
	    playersButton = new ArrayList<PlayerButton>();
	    // Create the Start Square
	    components.add(new GameSquare(gameSquares[0]));
	    // Add all the Players to Start and to the Player Array
	    for (Iterator<Player> itr = players.iterator(); itr.hasNext();) {
		playersButton.add(components.get(0).addNewPlayerButton(itr.next()));
	    }

	    // Add the Components to the GridBag Layout
	    for (int i = 1; i < gameSquares.length; i++) {
		components.add(new GameSquare(gameSquares[i]));
	    }

	    // Create an Iterator for the Components
	    Iterator<GameSquare> componentIterator = components.iterator();
	    Integer bottomRightComponent = 0;
	    //Add Panels for Each of the four sides
	    for (int sideIndex = 0; sideIndex < 4; sideIndex++) {
		for (int lineIndex = 0; lineIndex < LINE_SIZE; lineIndex++) {
		    // Get the Next Component
		    GameSquare component = componentIterator.next();
		    // Check the Side Index
		    switch (sideIndex) {
			case 0:
			    //top line
			    addComponent(lineIndex, 0, component);
			    break;
			case 1:
			    //right line
			    addComponent(LINE_SIZE, lineIndex, component);
			    break;
			case 2:
			    //bottom line - and in reverse order
			    bottomRightComponent = addComponent(LINE_SIZE - lineIndex, LINE_SIZE, component);
			    break;
			case 3:
			    //left line - and in reverse order
			    addComponent(0, LINE_SIZE - lineIndex, component);
			    break;
		    }
		}
	    }
	    // Invalidate the Board
	    invalidate();
	    // Main Inner Area Notice Starts at (1,1) and takes up 11x11
	    JPanel innerPanel = new JPanel(new BorderLayout());
	    // Add the Center Panel
	    pnlCenter = new CenterPanel();
	    // Check if to show the Panel
	    pnlCenter.setVisible(pnlBoard.isVisible());
	    // Add the Panel to the Inner Panel
	    innerPanel.add(pnlCenter, BorderLayout.CENTER);
	    // Set the Background Color of the Center Panel
	    innerPanel.setBackground(new Color(187, 223, 195));
	    // Set a Black Border
	    innerPanel.setBorder(BorderFactory.createLineBorder(Color.black));
	    // Get the Insets of the Component at The Bottom Right Place
	    Dimension pnlbottomRight = components.get(0).getPreferredSize();
	    Integer h = ((Double)(pnlbottomRight.getHeight()/9)).intValue();
	    Integer w = ((Double)(pnlbottomRight.getWidth()/9)).intValue();
	    Insets bottomRight = new Insets(1,1,h+20,w+20);

	    // Add the Inner Panel to the Game Board with the GridBag Constrains
	    pnlBoard.add(innerPanel,
		    new GridBagConstraints(1,
		    1,
		    LINE_SIZE + 1,
		    LINE_SIZE + 1,
		    0.1, 0.1,
		    GridBagConstraints.NORTHWEST,
		    GridBagConstraints.BOTH,
		    bottomRight,
		    3, 3));
	        // Invalidate the Board
	    invalidate();
	    // Repaint and Validate
	    Utils.repaintAndValidateAll(pnlBoard);
	}
    }

    /**
     * Adds a Component to the GridBagLayout - Thanks to Liron Blecher
     * @param gridX The X Position of the Component
     * @param gridY The Y Position of the Component
     * @param component The Component to be Added
     */
    private Integer addComponent(int gridX, int gridY, GameSquare component) {
	// Create a new Constrains Object
	GridBagConstraints c = new GridBagConstraints();
	// Set the Constrains
	c.gridx = gridX;
	c.gridy = gridY;
	c.weightx = 0.1;
	c.weighty = 0.1;
	c.anchor = GridBagConstraints.CENTER;
	c.fill = GridBagConstraints.BOTH;
	c.ipadx = 3;
	c.ipady = 3;
	c.insets = new Insets(1, 1, 1, 1);
	// Add the Component
	pnlBoard.add(component, c);
	return pnlBoard.getComponentCount();
    }

    /**
     * Set the Player Panel
     * @param player The Current Playing Player
     */
    public void setPlayer(Player player) {
	pnlPlayer.setPlayerDetails(player);
    }

    /**
     * Update The Game of the Current Player Event
     * @param player The Playing Player
     * @param currLocation The Current Square Location the Player is on
     */
    public void updatePlayer(Player player, Integer currLocation) {
	// Get the Game Square
	GameSquare square = components.get(currLocation);
	// Update the Player Funds
	pnlPlayer.updateFunds(player.getFunds());
	// Update the Player Get Out of Prison Card
	pnlPlayer.updateGetOutOfPrison(player.getRelease_from_prison_card());
	// Update the Square Owner
	square.updateOwner();
	// Repaint and Validate
	Utils.repaintAndValidateAll(pnlBoard);
    }

    /**
     * Show the Game Board
     */
    public void showBoard() {
	try {
	    // Repaint
	    repaint();
	    // Set the Visibility of the Game Board
	    setVisible(true);
	} catch (Exception e) {
	    Utils.writeText("Show Board Error!");
	    e.printStackTrace();
	}
    }

    /**
     * Set the Cubes Panel Pictures
     * @param cubes The Game Cubes
     */
    public void setCubes(Cube[] cubes) {
	// Set the Cubes Pictures
	pnlCenter.setCubes(cubes);
	// Repaint and Validate
	repaint();
	validate();
    }

    public CenterPanel getPnlCenter() {
	return pnlCenter;
    }

    /**
     * Moves a Player from one Square to the Other
     * @param player The Player to Move
     * @param newLocation The Square Index to move the player to
     */
    public void movePlayer(Integer playerIndex, Integer currLocation, Integer newLocation, Boolean moveNow) {
	GameSquare square;

	PlayerButton player = playersButton.get(playerIndex);

	// Check that the Number of Steps to Move is not Bigger than 12
	if(Math.abs(newLocation - currLocation) > 34){
	    // If it is - Spare the Animation, Move with no Animation
	    moveNow = true;
	}

	if (moveNow != true) {
	    if (currLocation < newLocation) {
		for (int i = currLocation; i < newLocation; ++i) {
		    // Set the Source Square
		    square = components.get(i);
		    // Remove the Player Button from the Source Panel
		    square.movePlayerOffSquare(player);

		    // Set the Destination Square
		    square = components.get(i + 1);
		    // Add the Player Button to the New Location
		    square.movePlayerToSquare(player);

		    // Repaint and validate
		    Utils.repaintAndValidateAll(square);
		}
	    } else {
		int i = currLocation;
		while (i % 36 != newLocation) {
		    Integer refSquareID = (i % 36);
		    // Set the Source Square
		    square = components.get(refSquareID);
		    // Remove the Player Button from the Source Panel
		    square.movePlayerOffSquare(player);
		    // Referance the Next Square
		    refSquareID = ((i + 1) % 36);
		    // Set the Destination Square
		    square = components.get(refSquareID);
		    // Add the Player Button to the New Location
		    square.movePlayerToSquare(player);
		    // Increment Index
		    ++i;
		    // Repaint and validate
		    Utils.repaintAndValidateAll(square);
		}
	    }
	} else {
	    // Set the Source Square
	    square = components.get(currLocation);
	    // Remove the Player Button from the Source Panel
	    square.movePlayerOffSquare(player);
	    // Set the Destination Square
	    square = components.get(newLocation);
	    // Add the Player Button to the New Location
	    square.movePlayerToSquare(player);
	    // Repaint and validate
	    Utils.repaintAndValidateAll(square);
	}
	// Repaint and validate
	Utils.repaintAndValidateAll(pnlBoard);
    }

    /**
     * Deletes the Player from the Game
     * @param playerIndex The Player index in the Game
     * @param currLocation The Deleted Player Current Location
     */
    public void deletePlayer(Integer playerIndex, Integer currLocation) {
	// Set the Current Square the Player is on
	GameSquare square = components.get(currLocation);
	// Bind to the Player Button
	PlayerButton player = playersButton.get(playerIndex);
	// Remove the Player off the Square
	square.movePlayerOffSquare(player);
	// Scan all Squares and remove the Ownership background
	for (GameSquare comp : components) {
	    comp.clearOwner(player.getColor());
	}
	// Hide the Player Button
	player.delete();
    }

    /**
     * Updates all the Board Components and Squares
     * @param cashier The Current Cashier Amount - to update the Cashier
     */
    public void updateAll(Double cashier) {
	// Update all the Squares
	for (GameSquare square : components) {
	    square.updateOwner();
	}
	// Update Cashier
	updateCashier(cashier);
	// Repaint and Validate Player
	Utils.repaintAndValidateAll(pnlPlayer);
	// Repaint and Validate Center
	Utils.repaintAndValidateAll(pnlCenter);
	// Repaint and Validate Board
	Utils.repaintAndValidateAll(pnlBoard);
    }

    /**
     * Adds another House to the Square the Player is On
     * @param currLocation The Index of the Square the Player is on
     */
    public void buyHouse(Integer currLocation) {
	// Add a House
	components.get(currLocation).addHouse();
	// Repaint and Validate the Square
	components.get(currLocation).repaint();
	components.get(currLocation).validate();
    }

    /**
     * Updates the Cashier Money Amount
     * @param cashier The Updated Cash Amount
     */
    public void updateCashier(Double cashier) {
	pnlCenter.setCash(cashier);
    }

    /**
     * Asks the User if he wants to exit the Game
     * This Function will close the entire game
     * @return The User Response in Window Constants
     */
    private int exitProgram() throws MonopolErrors {
	// Set the Popup Dialog Message
//	PopUpDialog popDialog = new PopUpDialog(this, "Are you sure you want to Exit the Game?", 10);

	// Ask player by swing if he wants to Quit the Game
//	if (popDialog.showPlayerDialog()) {
	if (Utils.showPlayerDialog("Are you sure you want to Exit the Game?")) {
	    Player player = pnlPlayer.getPlayer();
	    // Check that we have a Player in the Player Panel
	    if (player != null) {
		// Delete the Player Safly from the Game
		Monopol.deletePlayer(player);
	    }
	    return EXIT_ON_CLOSE;
	} else {
	    return DO_NOTHING_ON_CLOSE;
	}
    }
}
